package de.cau.apo.problems.travellingsalesman;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import de.cau.apo.problems.IInstance;

/**
 * This class represents an instance of the traveling salesman problem.
 * There is just a list of nodes that need to be visited in this instance.
 * 
 * @author Marius Richter
 *
 */
public class TSPInstance extends DefaultHandler implements IInstance {

	// ########### properties ###########
	
	/** the list of nodes that need to be visited */
	public List<Node> nodes = new ArrayList<Node>();
	
	// ########### instantiation ###########
	
	/**
	 * Default empty constructor
	 */
	public TSPInstance() {
	}
	
	// ########### getter/setter ###########
	
	/**
	 * @return the number of nodes that need to be visited
	 */
	public int getNumNodes() {
		return nodes.size();
	}
	
	/**
	 * @return the number of edges that exist
	 */
	public int getNumEdges() {
		return nodes.size() * (nodes.size() - 1);
	}
	
	/**
	 * @return the list of nodes that need to be visited
	 */
	public List<Node> getNodes() {
		return nodes;
	}
	
	/**
	 * @param index the index of a node
	 * @return the node that has the given index
	 */
	public Node getNode(int index) {
		Node node = nodes.get(index);
		if (node.getIndex() != index) {
			for (Node n: this.nodes) {
				if (n.getIndex() == index) {
					return n;
				}
			}
		}
		return node;
	}
	
	/**
	 * @param fromIndex the index of the from node
	 * @param toIndex the index of the to node
	 * @return the costs of the edge between the nodes indicated by fromIndex and toIndex
	 */
	public int getEdgeCosts(int fromIndex, int toIndex) {
		if (fromIndex == toIndex) {
			return 0;
		}
		return this.getNode(fromIndex).getEdgeCost(this.getNode(toIndex));
	}
	
	// ########### overriding DefaultHandler (xml parser) ###########
	
	@Override
	public boolean parseInstance(String instancePath) throws FileNotFoundException {
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			factory.setNamespaceAware(false);
			
			XMLReader xmlReader = factory.newSAXParser().getXMLReader();
			
			xmlReader.setContentHandler(this);
			xmlReader.parse(instancePath);
			
			return true;
		} catch (SAXException | ParserConfigurationException | IOException e) {
			return false;
		} 
	}
	
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		if (qName.equals("vertex")) {
			nodes.add(new Node(nodes.size()));
		} else if (qName.equals("edge")) {
			int costs = Integer.parseInt(attributes.getValue("cost"));
			nodes.get(nodes.size()-1).edges.put(null, costs);
		}
	}
	
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		Integer costs = nodes.get(nodes.size()-1).edges.get(null);
		if (costs != null) {
			nodes.get(nodes.size()-1).edges.put(
					Integer.parseInt(String.copyValueOf(ch, start, length)), costs);
			nodes.get(nodes.size()-1).edges.remove(null);
		}
	}

}
